package pzl.com.photowalldemo.adapter;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

import pzl.com.photowalldemo.R;
import pzl.com.photowalldemo.cache.DiskLruCache;
import pzl.com.photowalldemo.constant.Urls;
import pzl.com.photowalldemo.utils.BitmapUtil;
import pzl.com.photowalldemo.utils.IOUtil;
import pzl.com.photowalldemo.utils.MD5Util;
import pzl.com.photowalldemo.utils.SDUtil;

/**
 * @author zl.peng
 * @version [1.0, 2016-11-03]
 */
public class Adapter extends BaseAdapter implements AbsListView.OnScrollListener {
    private ExecutorService netPool = Executors.newFixedThreadPool(3);
    private ExecutorService diskTaskPool = Executors.newFixedThreadPool(8);
    static final String TAG = "Adapter";
    private LayoutInflater inflater;
    private Activity mContext;
    private List<ImgTask> imgTaskList;
    private int mFirstVisibleIte;
    private int mVisibleItemCount;
    private GridView mGv;

    private LruCache<String, Bitmap> mMemoryLruCache;
    private DiskLruCache mDiskLruCache;

    public Adapter(Activity context, GridView gv) {
        mContext = context;
        mGv = gv;
        inflater = LayoutInflater.from(mContext);
        //imgTaskList = Collections.synchronizedList(new ArrayList<ImgTask>());//以防ConcurrentModificationException
        imgTaskList =  new CopyOnWriteArrayList<ImgTask>();//以防ConcurrentModificationException

        initMemoryCache();
        initDiskCache();
    }

    /**
     * 初始化内存缓存
     */
    private void initMemoryCache() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheMemory = maxMemory / 4;
        mMemoryLruCache = new LruCache<String, Bitmap>(cacheMemory) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }

    /**
     * 初始化磁盘缓存
     */
    private void initDiskCache() {
        try {
            File cacheDir = SDUtil.getDiskCacheDir(mContext, "bitmap");
            Log.i(TAG, "cacheDir：" + cacheDir.toString());
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            /**
             * 目录
             * appVersion 默认升级个版本 磁盘缓存毁清空，并重新请求数据
             * valueCount 同一个key存多少份缓存（1）
             * 缓存 byte
             */
            mDiskLruCache = DiskLruCache.open(cacheDir, 1, 1, 20 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.i(TAG, "getView() position=" + position);
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_gridview, null, false);
        }
        ImageView iv = (ImageView) convertView.findViewById(R.id.photo);
        iv.setImageResource(R.drawable.default_pic);//复用恢复
        Bitmap bitmap = mMemoryLruCache.get(Urls.imgs[position]);
        if (bitmap != null) iv.setImageBitmap(bitmap);
        iv.setTag(position);
        iv.setTag(R.id.key_tag_url, Urls.imgs[position]);
        // Log.i(TAG, "Adapter.getView() 当前线程：" + Thread.currentThread().getName());
        return convertView;
    }

    @Override
    public int getCount() {
        return Urls.imgs.length;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.i(TAG, "scrollState=" + scrollState);
        if (scrollState == SCROLL_STATE_IDLE) {
            Log.i(TAG, "加载部分：" + mFirstVisibleIte + "," + mVisibleItemCount);
            for (int i = mFirstVisibleIte; i <= mFirstVisibleIte + mVisibleItemCount - 1; i++) {
                ImageView iv = (ImageView) mGv.findViewWithTag(i);

                String url = (String) iv.getTag(R.id.key_tag_url);
                Bitmap bitmapFromCache = mMemoryLruCache.get(url);
                if (bitmapFromCache == null) {
                    diskTaskPool.submit(new DiskCacheReadTask(url, iv));
                } else {
                    iv.setImageBitmap(bitmapFromCache);
                }

            }
        } else {
            Log.i(TAG, "取消全部任务");
            for (ImgTask temp : imgTaskList) {
                temp.cancel(true);
            }
            imgTaskList.clear();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleIte = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;
    }

    class ImgTask extends FutureTask<Bitmap> {
        ImgCallable mCallable;

        public ImgTask(ImgCallable callable) {
            super(callable);
            mCallable = callable;
        }


        @Override
        protected void done() {
            //Log.i(TAG, "FutureTask.done() 当前线程：" + Thread.currentThread().getName());
            super.done();
            if (isCancelled()) {
                Log.i(TAG, "cancell done");
                return;
            }
            Log.i(TAG, "completed done");

            Bitmap temp;
            try {
                temp = get();
                final Bitmap finalResult = temp;
                if (finalResult != null) {
                    Log.i(TAG, "写入内存<" + mCallable.getUrl() + ">");
                    mMemoryLruCache.put(mCallable.getUrl(), finalResult);
                    DiskCacheWriteTask diskCacheWriteTask = new DiskCacheWriteTask(mCallable.getUrl(), finalResult);
                    diskTaskPool.submit(diskCacheWriteTask);
                }
                mContext.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        boolean isNull = finalResult == null ? true : false;
                        if (isNull) {
                            mCallable.getImageView().setImageResource(R.drawable.error_pic);
                        } else {
                            mCallable.getImageView().setImageBitmap(finalResult);
                        }
                        //Log.i(TAG, "setImageBitmap " + (isNull ? "null" : "not null"));
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    class ImgCallable implements Callable<Bitmap> {
        private ImageView iv;
        private String url;

        public ImgCallable(ImageView photo) {
            iv = photo;
            url = (String) iv.getTag(R.id.key_tag_url);
        }

        /**
         * 这个call方法在FutureTask中貌似有自动识别并且换线程的功能（在主线程和用户线程自动切换）
         *
         * @return
         * @throws Exception
         */
        @Override
        public Bitmap call() throws Exception {
            //Log.i(TAG, "Callable.call() 当前线程：" + Thread.currentThread().getName());
            mContext.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    iv.setImageResource(R.drawable.loading_img);
                }
            });
            return BitmapUtil.downloadBitmap(url);
        }

        public ImageView getImageView() {
            return iv;
        }

        public String getUrl() {
            return url;
        }

    }

    /**
     * 磁盘缓存写数据task
     */
    class DiskCacheWriteTask implements Runnable {
        private String mUrl;
        private Bitmap mBitmap;

        public DiskCacheWriteTask(String url, Bitmap bitmap) {
            mUrl = url;
            mBitmap = bitmap;
        }

        @Override
        public void run() {
            try {
                String key = MD5Util.hashKeyForDisk(mUrl);
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor != null) {
                    OutputStream outputStream = editor.newOutputStream(0);
                    if (IOUtil.is2os(BitmapUtil.Bitmap2InputStream(mBitmap), outputStream)) {
                        editor.commit();
                        Log.i(TAG, "写入磁盘缓存<" + mUrl + ">");
                    } else {
                        editor.abort();
                    }
                }
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 磁盘缓存度数据task
     */
    class DiskCacheReadTask implements Runnable {
        private String mUrl;
        private ImageView mIv;

        public DiskCacheReadTask(String url, ImageView iv) {
            mUrl = url;
            mIv = iv;
        }

        @Override
        public void run() {
            try {
                String key = MD5Util.hashKeyForDisk(mUrl);
                DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
                if (snapShot != null) {
                    InputStream is = snapShot.getInputStream(0);
                    final Bitmap bitmap = BitmapFactory.decodeStream(is);
                    mMemoryLruCache.put(mUrl, bitmap);
                    mContext.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.i(TAG, "读到磁盘缓存<" + mUrl + ">");
                            mIv.setImageBitmap(bitmap);
                        }
                    });
                } else {
                    ImgCallable callable = new ImgCallable(mIv);
                    ImgTask imgTask = new ImgTask(callable);
                    imgTaskList.add(imgTask);
                    netPool.submit(imgTask);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void destroy() {
        imgTaskList.clear();
        imgTaskList = null;
        netPool.shutdownNow();
        netPool = null;
        mMemoryLruCache.evictAll();
    }

}
